package jmine.tec.script.impl;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import bancosys.tec.utils.reflection.ReflectionUtils;

import jmine.tec.script.ScriptExecutionContext;
import jmine.tec.script.ScriptParameters;

/**
 * Contexto de execução abstrato.
 * 
 * @author jrenaut
 * @param <R> O tipo do resultado
 */
public abstract class AbstractScriptExecutionContext<R> implements ScriptExecutionContext<R> {

    private final String name;

    private final String script;

    private final String initScript;

    private final Serializable scriptId;

    private final Long version;

    private final ScriptParameters parameters;

    /**
     * C'tor
     * 
     * @param initScript String
     * @param script String
     * @param scriptId Serializable
     * @param version Long
     */
    public AbstractScriptExecutionContext(final String initScript, final String script, final Serializable scriptId, final Long version) {
        this(initScript, script, scriptId, version, new HashMap<String, Object>());
    }

    /**
     * C'tor
     * 
     * @param initScript String
     * @param script String
     * @param scriptId Serializable
     * @param version Long
     * @param scriptParameters Map
     */
    public AbstractScriptExecutionContext(final String initScript, final String script, final Serializable scriptId, final Long version,
            final Map<String, Object> scriptParameters) {
        this("unnamed", initScript, script, scriptId, version, scriptParameters);
    }

    /**
     * C'tor
     * 
     * @param name String
     * @param initScript String
     * @param script String
     * @param scriptId Serializable
     * @param version Long
     * @param scriptParameters Map
     */
    public AbstractScriptExecutionContext(final String name, final String initScript, final String script, final Serializable scriptId,
            final Long version, final Map<String, Object> scriptParameters) {
        this(name, script, initScript, scriptId, version, scriptParameters, parseParameterTypes(scriptParameters),
                new HashMap<String, Object>());
    }

    /**
     * C'tor
     * 
     * @param name String
     * @param script String
     * @param initScript String
     * @param scriptId {@link Serializable}
     * @param version Long
     * @param scriptParameters map
     * @param scriptParameterTypes map
     * @param scriptConstants map
     */
    public AbstractScriptExecutionContext(String name, String script, String initScript, Serializable scriptId, Long version,
            Map<String, Object> scriptParameters, Map<String, Class<?>> scriptParameterTypes, Map<String, Object> scriptConstants) {
        this(name, script, initScript, scriptId, version, ScriptParametersImpl.fromMaps(scriptParameters, scriptParameterTypes,
                scriptConstants));
    }

    /**
     * C'tor
     * 
     * @param name String
     * @param script String
     * @param initScript String
     * @param scriptId {@link Serializable}
     * @param version Long
     * @param params params
     */
    public AbstractScriptExecutionContext(String name, String script, String initScript, Serializable scriptId, Long version,
            ScriptParameters params) {
        super();
        this.name = name;
        this.script = script;
        this.initScript = initScript;
        this.scriptId = scriptId;
        this.version = version;
        this.parameters = params;
    }

    /**
     * @return the script
     */
    public final String getScript() {
        return this.script;
    }

    /**
     * @return the initScript
     */
    public final String getInitScript() {
        return this.initScript;
    }

    /**
     * @return the scriptId
     */
    public final Serializable getScriptId() {
        return this.scriptId;
    }

    /**
     * @return the version
     */
    public final Long getVersion() {
        return this.version;
    }

    /**
     * @return the scriptParameters
     */
    public final Map<String, Object> getScriptParameters() {
        return this.parameters.getParameterValues();
    }

    /**
     * {@inheritDoc}
     * 
     * @see jmine.tec.script.ScriptExecutionContext#getScriptName()
     */
    public String getScriptName() {
        return this.name;
    }

    /**
     * {@inheritDoc}
     * 
     * @see jmine.tec.script.ScriptExecutionContext#getScriptParametersTypes()
     */
    public Map<String, Class<?>> getScriptParametersTypes() {
        return this.parameters.getParameterDefinitions();
    }

    /**
     * Extrai usando {@link Object#getClass()}
     * 
     * @param scriptParameters Map
     * @return Map
     */
    public static Map<String, Class<?>> parseParameterTypes(Map<String, Object> scriptParameters) {
        Map<String, Class<?>> map = new HashMap<String, Class<?>>(scriptParameters.size());
        Set<Entry<String, Object>> set = scriptParameters.entrySet();
        for (Entry<String, Object> entry : set) {
            Object obj = entry.getValue();
            if (ReflectionUtils.isEnumInstance(obj)) {
                map.put(entry.getKey(), ReflectionUtils.getEnumType(obj));
            } else {
                map.put(entry.getKey(), obj.getClass());
            }
        }
        return map;

    }

    /**
     * {@inheritDoc}
     * 
     * @see jmine.tec.script.ScriptExecutionContext#getScriptConstants()
     */
    public Map<String, Object> getScriptConstants() {
        return this.parameters.getConstants();
    }

    /**
     * Adiciona um objeto ao contexto
     * 
     * @param paramName string nome do objeto no contexto
     * @param parameter o objeto a ser adicionado
     * @param type o tipo do objeto a ser adicionado
     */
    protected void putParameter(String paramName, Object parameter, Class<?> type) {
        this.parameters.putParameter(paramName, parameter, type);
    }

    /**
     * @return the parameters
     */
    public ScriptParameters getParameters() {
        return parameters;
    }

}
